import { createClient, SupabaseClient } from '@supabase/supabase-js';
import winston from 'winston';
import { EmailService, SMTPConfig } from './EmailService';
import { EmailTemplateService } from './EmailTemplateService';
import { EmailTrackingService } from './EmailTrackingService';
import { PersonalizationEngine } from './PersonalizationEngine';
import { EmailPreferenceService } from './EmailPreferenceService';
import { EmailSchedulerService } from './EmailSchedulerService';
import { EmailComplianceService } from './EmailComplianceService';

export interface EmailSystemConfig {
  smtp: SMTPConfig;
  supabase: {
    url: string;
    key: string;
  };
  baseUrl: string;
  compliance?: {
    canSpamEnabled: boolean;
    gdprEnabled: boolean;
    doubleOptInEnabled: boolean;
  };
  logging: {
    level: string;
    format: string;
  };
}

export interface EmailJobResult {
  jobId: string;
  success: boolean;
  processed: number;
  errors: number;
  duration: number;
  details: {
    sent: number;
    failed: number;
    skipped: number;
    bounced: number;
  };
}

export class EmailOrchestrator {
  private supabase: SupabaseClient;
  private logger: winston.Logger;
  private emailService: EmailService;
  private templateService: EmailTemplateService;
  private trackingService: EmailTrackingService;
  private personalizationEngine: PersonalizationEngine;
  private preferenceService: EmailPreferenceService;
  private schedulerService: EmailSchedulerService;
  private complianceService: EmailComplianceService;
  
  private isInitialized = false;
  private config: EmailSystemConfig;

  constructor(config: EmailSystemConfig) {
    this.config = config;

    // Initialize Supabase client
    this.supabase = createClient(config.supabase.url, config.supabase.key);

    // Initialize logger
    this.logger = winston.createLogger({
      level: config.logging.level,
      format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.errors({ stack: true }),
        winston.format.json()
      ),
      transports: [
        new winston.transports.Console({
          format: winston.format.combine(
            winston.format.colorize(),
            winston.format.simple()
          )
        }),
        new winston.transports.File({
          filename: 'logs/email-system.log',
          maxsize: 5242880, // 5MB
          maxFiles: 10
        }),
        new winston.transports.File({
          filename: 'logs/email-errors.log',
          level: 'error',
          maxsize: 5242880,
          maxFiles: 5
        })
      ]
    });

    // Initialize all services
    this.initializeServices();
  }

  /**
   * Initialize all email services
   */
  private initializeServices(): void {
    try {
      // Core services
      this.emailService = new EmailService(
        this.config.smtp,
        this.config.supabase.url,
        this.config.supabase.key,
        this.logger
      );

      this.templateService = new EmailTemplateService(
        this.config.supabase.url,
        this.config.supabase.key,
        this.logger
      );

      this.trackingService = new EmailTrackingService(
        this.config.supabase.url,
        this.config.supabase.key,
        this.logger,
        this.config.baseUrl
      );

      this.personalizationEngine = new PersonalizationEngine(
        this.config.supabase.url,
        this.config.supabase.key,
        this.logger
      );

      this.preferenceService = new EmailPreferenceService(
        this.config.supabase.url,
        this.config.supabase.key,
        this.logger
      );

      this.complianceService = new EmailComplianceService(
        this.config.supabase.url,
        this.config.supabase.key,
        this.logger,
        {
          canSpam: { enabled: this.config.compliance?.canSpamEnabled ?? true },
          gdpr: { enabled: this.config.compliance?.gdprEnabled ?? true },
          doubleOptIn: { enabled: this.config.compliance?.doubleOptInEnabled ?? true }
        }
      );

      // Scheduler service (depends on other services)
      this.schedulerService = new EmailSchedulerService(
        this.config.supabase.url,
        this.config.supabase.key,
        this.logger,
        this.emailService,
        this.preferenceService
      );

      this.logger.info('Email orchestrator services initialized successfully');
    } catch (error) {
      this.logger.error('Failed to initialize email services:', error);
      throw error;
    }
  }

  /**
   * Start the email system
   */
  async start(): Promise<void> {
    if (this.isInitialized) {
      this.logger.warn('Email orchestrator is already running');
      return;
    }

    try {
      this.logger.info('Starting email orchestrator...');

      // Start scheduler
      await this.schedulerService.start();

      // Start background job processor
      await this.startJobProcessor();

      this.isInitialized = true;
      this.logger.info('Email orchestrator started successfully');
    } catch (error) {
      this.logger.error('Failed to start email orchestrator:', error);
      throw error;
    }
  }

  /**
   * Stop the email system
   */
  async stop(): Promise<void> {
    if (!this.isInitialized) {
      this.logger.warn('Email orchestrator is not running');
      return;
    }

    try {
      this.logger.info('Stopping email orchestrator...');

      // Stop scheduler
      await this.schedulerService.stop();

      // Close email service
      await this.emailService.close();

      this.isInitialized = false;
      this.logger.info('Email orchestrator stopped successfully');
    } catch (error) {
      this.logger.error('Failed to stop email orchestrator:', error);
      throw error;
    }
  }

  /**
   * Send immediate email (bypassing scheduler)
   */
  async sendImmediateEmail(
    userId: string,
    templateType: string,
    templateData?: Record<string, any>,
    options?: {
      skipCompliance?: boolean;
      priority?: 'high' | 'normal' | 'low';
    }
  ): Promise<{
    success: boolean;
    messageId?: string;
    error?: string;
    complianceIssues?: string[];
  }> {
    try {
      // Check compliance if not skipped
      if (!options?.skipCompliance) {
        const complianceCheck = await this.complianceService.checkSendingCompliance(userId);
        if (!complianceCheck.canSend) {
          return {
            success: false,
            error: 'Compliance check failed',
            complianceIssues: complianceCheck.reasons
          };
        }
      }

      // Send email
      const result = await this.emailService.sendPersonalizedEmail(
        userId,
        templateType,
        templateData
      );

      if (result.success) {
        this.logger.info(`Immediate email sent successfully: ${userId} - ${templateType}`);
        
        // Update user engagement metrics
        await this.updateEngagementMetrics(userId, 'email_sent', {
          templateType,
          messageId: result.messageId
        });
      }

      return {
        success: result.success,
        messageId: result.messageId,
        error: result.error
      };

    } catch (error) {
      this.logger.error(`Failed to send immediate email to ${userId}:`, error);
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Unknown error'
      };
    }
  }

  /**
   * Send bulk emails to multiple users
   */
  async sendBulkEmails(
    emails: Array<{
      userId: string;
      templateType: string;
      templateData?: Record<string, any>;
    }>,
    options?: {
      batchSize?: number;
      delayBetweenBatches?: number;
      skipCompliance?: boolean;
    }
  ): Promise<EmailJobResult> {
    const startTime = Date.now();
    const jobId = `bulk_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    
    let processed = 0;
    let sent = 0;
    let failed = 0;
    let skipped = 0;
    let bounced = 0;

    try {
      this.logger.info(`Starting bulk email job ${jobId} for ${emails.length} recipients`);

      // Filter emails based on compliance if not skipped
      let filteredEmails = emails;
      if (!options?.skipCompliance) {
        filteredEmails = [];
        for (const email of emails) {
          const complianceCheck = await this.complianceService.checkSendingCompliance(email.userId);
          if (complianceCheck.canSend) {
            filteredEmails.push(email);
          } else {
            skipped++;
            this.logger.debug(`Skipped email for ${email.userId}: ${complianceCheck.reasons.join(', ')}`);
          }
        }
      }

      // Send emails in batches
      const batchSize = options?.batchSize || 100;
      const delay = options?.delayBetweenBatches || 1000;

      const results = await this.emailService.sendBulkEmails(
        filteredEmails,
        batchSize,
        delay
      );

      // Process results
      results.forEach(result => {
        processed++;
        if (result.success) {
          sent++;
        } else {
          failed++;
          if (result.error?.toLowerCase().includes('bounce')) {
            bounced++;
          }
        }
      });

      const duration = Date.now() - startTime;

      this.logger.info(`Bulk email job ${jobId} completed: ${sent} sent, ${failed} failed, ${skipped} skipped in ${duration}ms`);

      return {
        jobId,
        success: true,
        processed,
        errors: failed,
        duration,
        details: {
          sent,
          failed,
          skipped,
          bounced
        }
      };

    } catch (error) {
      const duration = Date.now() - startTime;
      this.logger.error(`Bulk email job ${jobId} failed:`, error);

      return {
        jobId,
        success: false,
        processed,
        errors: failed + 1,
        duration,
        details: {
          sent,
          failed: failed + 1,
          skipped,
          bounced
        }
      };
    }
  }

  /**
   * Subscribe user with compliance checks
   */
  async subscribeUser(
    userId: string,
    email: string,
    preferences?: {
      categories?: string[];
      keywords?: string[];
      frequency?: string;
    },
    metadata?: {
      source?: string;
      ipAddress?: string;
      userAgent?: string;
      campaign?: string;
    }
  ): Promise<{
    success: boolean;
    requiresConfirmation?: boolean;
    confirmationToken?: string;
    error?: string;
  }> {
    try {
      // Record consent for compliance
      const consentResult = await this.complianceService.recordConsent(
        userId,
        email,
        {
          source: metadata?.source || 'api',
          ipAddress: metadata?.ipAddress,
          userAgent: metadata?.userAgent,
          campaign: metadata?.campaign
        }
      );

      if (!consentResult.success) {
        return {
          success: false,
          error: 'Failed to record consent'
        };
      }

      // Subscribe user to email preferences
      const subscribeResult = await this.preferenceService.subscribeUser(
        userId,
        {
          categories: preferences?.categories,
          keywords: preferences?.keywords,
          frequency: preferences?.frequency as any
        }
      );

      if (!subscribeResult.success) {
        return {
          success: false,
          error: subscribeResult.error
        };
      }

      // Start welcome series if user is confirmed
      if (!consentResult.requiresDoubleOptIn) {
        await this.schedulerService.startWelcomeSeriesForUser(userId);
      }

      this.logger.info(`User subscribed successfully: ${userId} (${email})`);

      return {
        success: true,
        requiresConfirmation: consentResult.requiresDoubleOptIn,
        confirmationToken: consentResult.confirmationToken
      };

    } catch (error) {
      this.logger.error(`Failed to subscribe user ${userId}:`, error);
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Unknown error'
      };
    }
  }

  /**
   * Unsubscribe user
   */
  async unsubscribeUser(
    userId: string,
    method: 'email_link' | 'web_form' | 'api' = 'api',
    reasons?: string[]
  ): Promise<{ success: boolean; error?: string }> {
    try {
      // Process opt-out for compliance
      const optOutResult = await this.complianceService.processOptOut(
        userId,
        method,
        { reasons }
      );

      if (!optOutResult.success) {
        return optOutResult;
      }

      // Update preferences
      const unsubscribeResult = await this.preferenceService.unsubscribeUser(
        userId,
        'global',
        undefined,
        reasons || [],
        method
      );

      this.logger.info(`User unsubscribed: ${userId} via ${method}`);
      return unsubscribeResult;

    } catch (error) {
      this.logger.error(`Failed to unsubscribe user ${userId}:`, error);
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Unknown error'
      };
    }
  }

  /**
   * Get comprehensive email analytics
   */
  async getEmailAnalytics(
    startDate: Date,
    endDate: Date,
    userId?: string
  ): Promise<{
    overview: {
      totalEmails: number;
      deliveryRate: number;
      openRate: number;
      clickRate: number;
      unsubscribeRate: number;
      bounceRate: number;
    };
    trends: Array<{
      date: string;
      sent: number;
      delivered: number;
      opened: number;
      clicked: number;
    }>;
    templatePerformance: Array<{
      template: string;
      sent: number;
      openRate: number;
      clickRate: number;
    }>;
    userEngagement?: {
      mostEngaged: Array<{
        userId: string;
        email: string;
        opens: number;
        clicks: number;
        lastActivity: string;
      }>;
      leastEngaged: Array<{
        userId: string;
        email: string;
        daysSinceLastOpen: number;
      }>;
    };
  }> {
    try {
      // Get tracking metrics
      const trackingMetrics = await this.trackingService.getTrackingMetrics(
        startDate,
        endDate,
        userId
      );

      // Get delivery stats
      const deliveryStats = await this.emailService.getDeliveryStats(
        startDate,
        endDate,
        userId
      );

      // Get template performance data
      const { data: templateData } = await this.supabase
        .from('email_digests')
        .select('digest_type, is_sent, open_count, click_count')
        .gte('generation_time', startDate.toISOString())
        .lte('generation_time', endDate.toISOString());

      const templatePerformance = this.aggregateTemplatePerformance(templateData || []);

      // Get user engagement data if not specific user
      let userEngagement;
      if (!userId) {
        const trackingReport = await this.trackingService.generateTrackingReport(
          startDate,
          endDate
        );
        userEngagement = {
          mostEngaged: trackingReport.userEngagement.slice(0, 10),
          leastEngaged: [] // Would be populated with inactive users
        };
      }

      return {
        overview: {
          totalEmails: trackingMetrics.totalSent,
          deliveryRate: trackingMetrics.deliveryRate,
          openRate: trackingMetrics.openRate,
          clickRate: trackingMetrics.clickRate,
          unsubscribeRate: trackingMetrics.unsubscribeRate,
          bounceRate: trackingMetrics.bounceRate
        },
        trends: [], // Would be populated with daily/weekly trend data
        templatePerformance,
        userEngagement
      };

    } catch (error) {
      this.logger.error('Failed to get email analytics:', error);
      throw error;
    }
  }

  /**
   * Handle webhook events (bounces, complaints, etc.)
   */
  async handleWebhookEvent(
    eventType: 'bounce' | 'complaint' | 'delivery' | 'open' | 'click',
    eventData: Record<string, any>
  ): Promise<{ success: boolean; error?: string }> {
    try {
      this.logger.info(`Handling webhook event: ${eventType}`, eventData);

      switch (eventType) {
        case 'bounce':
          await this.trackingService.trackEmailBounce(
            eventData.digestId,
            eventData.userId,
            eventData.bounceType,
            eventData.bounceReason,
            eventData
          );
          break;

        case 'complaint':
          // Mark as spam complaint and suppress user
          await this.supabase
            .from('email_suppression_list')
            .upsert({
              user_id: eventData.userId,
              email: eventData.email,
              reason: 'spam_complaint',
              source: 'webhook',
              is_active: true,
              added_at: new Date().toISOString()
            });
          break;

        case 'delivery':
          await this.supabase
            .from('email_delivery_logs')
            .update({ delivery_status: 'delivered' })
            .eq('provider_message_id', eventData.messageId);
          break;

        case 'open':
          if (eventData.trackingToken) {
            await this.trackingService.trackEmailOpen(
              eventData.trackingToken,
              eventData.userAgent,
              eventData.ipAddress
            );
          }
          break;

        case 'click':
          if (eventData.trackingToken) {
            await this.trackingService.trackEmailClick(
              eventData.trackingToken,
              eventData.userAgent,
              eventData.ipAddress,
              eventData.referrer
            );
          }
          break;
      }

      return { success: true };

    } catch (error) {
      this.logger.error(`Failed to handle webhook event ${eventType}:`, error);
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Unknown error'
      };
    }
  }

  /**
   * Get system status and health metrics
   */
  async getSystemStatus(): Promise<{
    status: 'healthy' | 'degraded' | 'down';
    services: Record<string, 'up' | 'down'>;
    metrics: {
      queueLength: number;
      avgProcessingTime: number;
      errorRate: number;
      lastSuccessfulDelivery: string;
    };
    issues: string[];
  }> {
    const issues: string[] = [];
    const services: Record<string, 'up' | 'down'> = {};

    try {
      // Check database connectivity
      const { error: dbError } = await this.supabase
        .from('users')
        .select('user_id')
        .limit(1);
      
      services.database = dbError ? 'down' : 'up';
      if (dbError) issues.push('Database connectivity issues');

      // Check email service
      services.emailService = this.isInitialized ? 'up' : 'down';
      if (!this.isInitialized) issues.push('Email service not initialized');

      // Check recent delivery performance
      const { data: recentDeliveries } = await this.supabase
        .from('email_delivery_logs')
        .select('delivery_status, delivery_timestamp')
        .gte('delivery_timestamp', new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString())
        .order('delivery_timestamp', { ascending: false })
        .limit(100);

      const successfulDeliveries = recentDeliveries?.filter(d => 
        d.delivery_status === 'delivered' || d.delivery_status === 'opened'
      ) || [];

      const errorRate = recentDeliveries ? 
        ((recentDeliveries.length - successfulDeliveries.length) / recentDeliveries.length) * 100 : 0;

      if (errorRate > 10) issues.push('High delivery error rate');

      // Get queue length
      const { data: queuedJobs } = await this.supabase
        .from('background_jobs')
        .select('job_id')
        .in('job_status', ['pending', 'running']);

      const queueLength = queuedJobs?.length || 0;
      if (queueLength > 1000) issues.push('High job queue length');

      const status = issues.length === 0 ? 'healthy' : 
                   issues.length <= 2 ? 'degraded' : 'down';

      return {
        status,
        services,
        metrics: {
          queueLength,
          avgProcessingTime: 0, // Would calculate from job execution times
          errorRate,
          lastSuccessfulDelivery: successfulDeliveries[0]?.delivery_timestamp || 'unknown'
        },
        issues
      };

    } catch (error) {
      this.logger.error('Failed to get system status:', error);
      return {
        status: 'down',
        services: { system: 'down' },
        metrics: { queueLength: 0, avgProcessingTime: 0, errorRate: 100, lastSuccessfulDelivery: 'unknown' },
        issues: ['System health check failed']
      };
    }
  }

  /**
   * Private helper methods
   */
  private async startJobProcessor(): Promise<void> {
    // Background job processor would be implemented here
    // This would handle queued email jobs, retries, etc.
    this.logger.info('Background job processor started');
  }

  private async updateEngagementMetrics(
    userId: string,
    eventType: string,
    metadata: Record<string, any>
  ): Promise<void> {
    try {
      await this.supabase
        .from('user_analytics')
        .upsert({
          user_id: userId,
          date: new Date().toISOString().split('T')[0],
          metric_name: `email_${eventType}`,
          metric_value: 1,
          metadata,
          created_at: new Date().toISOString()
        });
    } catch (error) {
      this.logger.error(`Failed to update engagement metrics for ${userId}:`, error);
    }
  }

  private aggregateTemplatePerformance(templateData: any[]): Array<{
    template: string;
    sent: number;
    openRate: number;
    clickRate: number;
  }> {
    const aggregated = new Map<string, { sent: number; opens: number; clicks: number }>();

    templateData.forEach(item => {
      const template = item.digest_type;
      const existing = aggregated.get(template) || { sent: 0, opens: 0, clicks: 0 };
      
      if (item.is_sent) {
        existing.sent++;
        existing.opens += item.open_count || 0;
        existing.clicks += item.click_count || 0;
      }
      
      aggregated.set(template, existing);
    });

    return Array.from(aggregated.entries()).map(([template, stats]) => ({
      template,
      sent: stats.sent,
      openRate: stats.sent > 0 ? (stats.opens / stats.sent) * 100 : 0,
      clickRate: stats.opens > 0 ? (stats.clicks / stats.opens) * 100 : 0
    }));
  }
}